//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;

namespace OpenLS.Spreadsheet
{
    class RangeOperations
    {
        static internal SpreadsheetDocument RangeToWorkbook (Range range)
        {
            Contract.Ensures(Contract.Result<SpreadsheetDocument>() != null);
#if DEBUG
            Contract.Ensures(Contract.Result<SpreadsheetDocument>().Workbook.CheckFormattingRecords());
#endif
            var result = new SpreadsheetDocument(range.Worksheet.Workbook.Document.Context);
            var workbook = new Workbook (result) {UndoEnabled = false};
            result.Workbook = workbook;
#if DEBUG
            workbook.CheckFormattingRecords();
#endif
            workbook.CalculationProperties.CalculationMode = CalculationMode.Manual;
            var sourceWorksheet = range.Worksheet;
            var worksheet = new Worksheet(workbook, sourceWorksheet.Name);
            worksheet.CopyGuid(sourceWorksheet);
            HashList<StyleFormattingRecord> styleRecords = new HashList<StyleFormattingRecord>();
            Action<CellFormattingRecord> addFn = r =>
                                                     {
                                                         if (r == null)
                                                             return;
                                                         if (r.Parent == null)
                                                             return;
                                                         if (styleRecords.Contains(r.Parent))
                                                             return;
                                                         styleRecords.Add(r.Parent);
                                                     };
            Dictionary<CellFormattingRecord, CellFormattingRecord> converterTable = new Dictionary<CellFormattingRecord, CellFormattingRecord>();
            Func<CellFormattingRecord, CellFormattingRecord> converter = (r) =>
                                                                             {
                                                                                 if (r == null)
                                                                                     return null;
                                                                                 CellFormattingRecord rr;
                                                                                 if (converterTable.TryGetValue(r,
                                                                                                                out rr))
                                                                                     return rr;
                                                                                 rr = r.CloneFor(workbook);
                                                                                 converterTable.Add(r, rr);
                                                                                 return rr;

                                                                             };
            foreach (Cell c in range.GetNonEmptyCells())
            {
                Cell c2 = worksheet[c.Address];
                c2.Value = c.Value;
                var newFormattirgRecord = converter(c.FormattingRecord);
                c2.SetFormattingRecordSafe(newFormattirgRecord);
                addFn(newFormattirgRecord);
                if (c.Formula != null)
                {
                    c2.Formula = c.Formula;
                }
            }
            var columns = Enumerable.Where(range.GetNonEmptyColumns(),
                                           c => c.FormattingRecord != null || c.CharacterWidth != null);
            foreach (Column column in columns)
            {
                Column column2 = worksheet.Columns[column.Index];
                var newFormattingRecord = converter(column.FormattingRecord);
                if (newFormattingRecord != null)
                    column2.SetStyleRecordSafe(newFormattingRecord);
                addFn(newFormattingRecord);

                column2.CharacterWidth = column.CharacterWidth;
            }
            var rows = Enumerable.Where(range.GetNonEmptyRows(), c => c.FormattingRecord != null || c.Height != null);
            foreach (Row row in rows)
            {
                Row row2 = worksheet.Rows[row.Index];
                var newFormattingRecord = converter(row.FormattingRecord);
                if (newFormattingRecord != null)
                    row2.SetStyleRecordSafe(newFormattingRecord);
                addFn(newFormattingRecord);

                row2.Height = row.Height;
            }
            foreach (var sr in styleRecords)
            {
                if (!sr.Style.IsBuiltIn)
                workbook.StyleSheet.AddStyleRecord(sr);
            }
#if DEBUG
            workbook.CheckFormattingRecords();
#endif

            List<Table> tables = new List<Table>(range.Worksheet.Tables).FindAll(t => range.Contains(t.Range));
            foreach(Table table in tables)
            {
                Range newRange = table.SimpleRange.ProjectedSimple(worksheet, new CellRelativeAddress(0, 0));
                worksheet.AddTable( newRange, table.Style, table.Name, table.Comment, table.IsPublic, table.IsReadOnly, table.AllowsUpdate, table.AllowsDelete, table.AllowsInsert);
            }
            List<DataValidation> dataValidations =
                Enumerable.ToList(Enumerable.Where(range.Worksheet.DataValidations, t => Range.Intersects(range, t.Range)));
            foreach (DataValidation dataValidation in dataValidations)
            {
                worksheet.DataValidations.Add(dataValidation.Projected(worksheet, new CellRelativeAddress()));
            }
            List<ConditionalFormatting> conditionalFormattings =
                Enumerable.ToList(Enumerable.Where(range.Worksheet.ConditionalFormatting, t => Range.Intersects(range, t.Range)));
            foreach (var conditionalFormatting in conditionalFormattings)
            {
                //\\add conditional formatting
            }

            worksheet.Selection = RangeExtensions.Projected(range, worksheet);
            workbook.Sheets.Add(worksheet);
            foreach (var ws in Enumerable.Where(Enumerable.OfType<Worksheet>(sourceWorksheet.Workbook.Sheets), ws => ws != sourceWorksheet))
                workbook.Sheets.Add(new Worksheet(workbook, ws.Name));
#if DEBUG
            workbook.CheckFormattingRecords();
#endif
            return result;
        }
        static internal void CopyCells(Range source, Range destination, BinaryAction<Cell, Cell> fn)
        {
            CellRelativeAddress offset = destination.Address.TopLeft - source.Address.TopLeft;
            if (source is FullRange)
            {
                if (destination.GetType() != source.GetType())
                {
                    Debug.WriteLine("Unable to copy cells");
                    return;
                }
                foreach (Cell cell in destination.GetNonEmptyCells())
                    cell.ClearContent();
                foreach (Cell cell in source.GetNonEmptyCells())
                    fn(cell, destination.Worksheet[cell.Address + offset]);
                return;

            }
            foreach (Cell cell in source.GetAllCells())
            {
                fn(cell, destination.Worksheet[cell.Address + offset]);
            }
        }
    }
}
